"""
Celery 异步任务模块
"""

from celery import current_task
from celery.exceptions import Retry
from ..celery_app import celery_app
from .services.convert_service import ConvertService
from .services.task_service import TaskService
from .services.websocket_service import WebSocketService
from .utils.file_utils import cleanup_directory
from .config import Config
from pathlib import Path
import logging

logger = logging.getLogger(__name__)


@celery_app.task(bind=True, max_retries=3, default_retry_delay=60)
def convert_document(self, task_id: str, file_path: str, options: dict):
    """
    异步文档转换任务
    
    Args:
        task_id: 任务ID
        file_path: 输入文件路径
        options: 转换选项
    """
    try:
        # 更新任务状态为处理中
        task_service = TaskService()
        task_service.update_task_progress(task_id, 0, 'processing', '开始转换')
        
        # 发送WebSocket进度更新
        WebSocketService.emit_progress_update(
            task_id=task_id,
            progress=0,
            status='processing',
            current_step='开始转换'
        )
        
        # 执行转换
        convert_service = ConvertService()
        
        # 更新进度：准备转换
        task_service.update_task_progress(task_id, 10, 'processing', '准备转换环境')
        WebSocketService.emit_progress_update(
            task_id=task_id,
            progress=10,
            status='processing',
            current_step='准备转换环境'
        )
        
        # 执行实际转换
        output_path = convert_service.convert_document(
            input_path=Path(file_path),
            output_format=options.get('output_format', 'docx'),
            template=options.get('template'),
            css_theme=options.get('css_theme'),
            page_size=options.get('page_size'),
            include_toc=options.get('include_toc', False),
            progress_callback=lambda progress, step: _update_conversion_progress(
                task_id, progress, step
            )
        )
        
        # 转换完成
        task_service.update_task_progress(task_id, 90, 'processing', '生成质量报告')
        WebSocketService.emit_progress_update(
            task_id=task_id,
            progress=90,
            status='processing',
            current_step='生成质量报告'
        )
        
        # 计算质量评分（简化版本）
        quality_score = _calculate_quality_score(output_path, options)
        
        # 任务完成
        download_url = f"/api/download/{task_id}"
        task_service.complete_task(
            task_id=task_id,
            output_path=str(output_path),
            quality_score=quality_score,
            download_url=download_url
        )
        
        # 发送完成通知
        WebSocketService.emit_task_complete(
            task_id=task_id,
            status='completed',
            download_url=download_url,
            quality_score=quality_score
        )
        
        logger.info(f"任务 {task_id} 转换完成，输出文件: {output_path}")
        return {
            'task_id': task_id,
            'output_path': str(output_path),
            'quality_score': quality_score,
            'download_url': download_url
        }
        
    except Exception as exc:
        logger.error(f"任务 {task_id} 转换失败: {str(exc)}")
        
        # 更新任务状态为失败
        task_service = TaskService()
        task_service.fail_task(task_id, str(exc))
        
        # 发送失败通知
        WebSocketService.emit_task_complete(
            task_id=task_id,
            status='failed',
            error_message=str(exc)
        )
        
        # 重试逻辑
        if self.request.retries < self.max_retries:
            logger.info(f"任务 {task_id} 准备重试，当前重试次数: {self.request.retries}")
            raise self.retry(exc=exc, countdown=60 * (self.request.retries + 1))
        
        raise exc


@celery_app.task
def cleanup_files():
    """
    定期清理过期文件任务
    """
    try:
        logger.info("开始清理过期文件")
        
        # 清理上传文件
        uploads_cleaned = cleanup_directory(
            Path(Config.UPLOAD_FOLDER),
            max_age_hours=Config.FILE_RETENTION_HOURS
        )
        
        # 清理输出文件
        outputs_cleaned = cleanup_directory(
            Path(Config.OUTPUT_FOLDER),
            max_age_hours=Config.FILE_RETENTION_HOURS
        )
        
        # 清理临时文件
        temp_cleaned = cleanup_directory(
            Path(Config.TEMP_FOLDER),
            max_age_hours=24  # 临时文件保留24小时
        )
        
        # 清理数据库中的过期任务记录
        task_service = TaskService()
        db_cleaned = task_service.cleanup_completed_tasks()
        
        total_cleaned = uploads_cleaned + outputs_cleaned + temp_cleaned + db_cleaned
        
        logger.info(f"文件清理完成，共清理 {total_cleaned} 个项目")
        return {
            'uploads_cleaned': uploads_cleaned,
            'outputs_cleaned': outputs_cleaned,
            'temp_cleaned': temp_cleaned,
            'db_records_cleaned': db_cleaned,
            'total_cleaned': total_cleaned
        }
        
    except Exception as exc:
        logger.error(f"文件清理失败: {str(exc)}")
        raise exc


def _update_conversion_progress(task_id: str, progress: int, step: str):
    """
    更新转换进度的回调函数
    
    Args:
        task_id: 任务ID
        progress: 进度百分比
        step: 当前步骤描述
    """
    try:
        # 调整进度范围到10-90之间
        adjusted_progress = 10 + int(progress * 0.8)
        
        task_service = TaskService()
        task_service.update_task_progress(task_id, adjusted_progress, 'processing', step)
        
        WebSocketService.emit_progress_update(
            task_id=task_id,
            progress=adjusted_progress,
            status='processing',
            current_step=step
        )
        
    except Exception as exc:
        logger.warning(f"更新进度失败: {str(exc)}")


def _calculate_quality_score(output_path: Path, options: dict) -> int:
    """
    计算文档质量评分
    
    Args:
        output_path: 输出文件路径
        options: 转换选项
        
    Returns:
        int: 质量评分 (0-100)
    """
    try:
        score = 85  # 基础分数
        
        # 根据文件大小调整分数
        if output_path.exists():
            file_size = output_path.stat().st_size
            if file_size > 1024 * 1024:  # 大于1MB
                score += 5
            elif file_size < 1024:  # 小于1KB
                score -= 10
        
        # 根据转换选项调整分数
        if options.get('template'):
            score += 3
        if options.get('css_theme'):
            score += 2
        if options.get('include_toc'):
            score += 5
        
        # 确保分数在合理范围内
        return max(0, min(100, score))
        
    except Exception:
        return 75  # 默认分数